package com.xuyiwei.xblog.business.article.service.impl;

import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.xuyiwei.xblog.business.article.dao.*;
import com.xuyiwei.xblog.business.article.model.*;
import com.xuyiwei.xblog.business.article.result.article.input.ArticleConditionVO;
import com.xuyiwei.xblog.business.article.result.article.out.ArticleDto;
import com.xuyiwei.xblog.business.article.result.article.out.CommentDto;
import com.xuyiwei.xblog.business.article.service.IArticleService;
import com.xuyiwei.xblog.business.article.service.IArticleTagsService;
import com.xuyiwei.xblog.core.base.constant.enums.ArticleStatusEnum;
import com.xuyiwei.xblog.core.base.constant.enums.CommonStatusEnum;
import com.xuyiwei.xblog.core.base.constant.enums.FileUploadType;
import com.xuyiwei.xblog.core.common.constant.factory.ConstantFactory;
import com.xuyiwei.xblog.core.common.constant.factory.PageFactory;
import com.xuyiwei.xblog.core.exception.GunsException;
import com.xuyiwei.xblog.core.shiro.ShiroKit;
import com.xuyiwei.xblog.core.shiro.ShiroUser;
import com.xuyiwei.xblog.core.support.HttpKit;
import com.xuyiwei.xblog.core.support.StrKit;
import com.xuyiwei.xblog.core.util.FileUtil;
import com.xuyiwei.xblog.core.util.IpUtil;
import com.xuyiwei.xblog.core.util.OssUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 文章表 服务实现类
 * </p>
 *
 * @author xuyiwei
 * @since 2018-10-22
 */
@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements IArticleService {

    @Autowired
    private ArticleMapper articleMapper;

    @Autowired
    private ArticleLoveMapper articleLoveMapper;

    @Autowired
    private ArticleOpposeMapper articleOpposeMapper;

    @Autowired
    private IArticleTagsService articleTagsService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private TagsMapper tagsMapper;

    @Override
    public List<String> listMaterial() {
        List<String> list = articleMapper.listMaterial();
        return list;
    }

    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void publish(Article article, Long[] tags, MultipartFile file) throws Exception {
        article.setCreateTime(new Date());
        article.setUpdateTime(new Date());
        article.setStatus(CommonStatusEnum.USE.getKey());

        if (null == tags || tags.length <= 0) {
            throw new GunsException("500", "请至少选择一个标签");
        }
        if (null != file) {
            String filePath = FileUtil.uploadToAlioss(file, FileUploadType.COVER_IMAGE, true);
            // 保存封面图片
            article.setCoverImage(filePath);
        }
        Long articleId = null;
        if ((articleId = article.getId()) != null) {
            baseMapper.updateById(article);
        } else {
            article.setUserId(ShiroKit.getUser().getId());
            baseMapper.insert(article);
            articleId = article.getId();
        }
        if (articleId != null) {
            articleTagsService.removeByArticleId(articleId);
            articleTagsService.insertList(tags, articleId);
        }

    }


    @Override
    public ArticleDto selectArticleById(Long articleId) {
        ArticleDto dto = articleMapper.selectArticleDetail(articleId);
        if (dto != null && StrKit.notBlank(dto.getCoverImage())) {
            dto.setCoverImage(dto.getCoverImage());
            dto.setCoverImageUrl(OssUtil.getUrl(dto.getCoverImage()));
        }
        return dto;
    }

    @Override
    @Transactional(readOnly = false)
    public void top(boolean top, List<Long> ids) {
        for (Long id : ids) {
            Article article = new Article();
            article.setId(id);
            article.setTop(top);
            article.updateById();
        }
    }

    @Override
    @Transactional(readOnly = false)
    public void recommend(boolean recommend, List<Long> ids) {
        for (Long id : ids) {
            Article article = new Article();
            article.setId(id);
            article.setRecommended(recommend);
            article.updateById();
        }
    }

    @Override
    @Transactional(readOnly = false)
    public void punishArticle(List<Long> ids) {
        articleMapper.punishArticle(ids);
    }

    @Override
    public List<ArticleDto> findPageByCondition(Page page, ArticleConditionVO condition, String orderByField, boolean isAsc) {
        List<ArticleDto> list = articleMapper.findPageByCondition(page, condition, orderByField, isAsc);
        for(ArticleDto dto:list){
            ConstantFactory.me().getArticlePage(dto);
        }
        return list;
    }

    @Override
    public List<ArticleDto> listRecommended(int pageSize) {
        ArticleConditionVO vo = new ArticleConditionVO();
        vo.setRecommended(true);
        vo.setStatus(ArticleStatusEnum.PUNISH.getKey());
        Page<ArticleDto> page = new PageFactory<ArticleDto>().defaultPage();
        page.setSize(pageSize);
        return findPageByCondition(page, vo, page.getOrderByField(), page.isAsc());
    }

    @Override
    public List<ArticleDto> listRecent(int pageSize) {
        ArticleConditionVO vo = new ArticleConditionVO();
        vo.setStatus(ArticleStatusEnum.PUNISH.getKey());
        Page<ArticleDto> page = new PageFactory<ArticleDto>().defaultPage();
        page.setSize(pageSize);
        return findPageByCondition(page, vo, page.getOrderByField(), page.isAsc());
    }

    @Override
    public List<ArticleDto> listRandom(int pageSize) {
        ArticleConditionVO vo = new ArticleConditionVO();
        vo.setStatus(ArticleStatusEnum.PUNISH.getKey());
        vo.setRandom(true);
        Page<ArticleDto> page = new PageFactory<ArticleDto>().defaultPage();
        page.setSize(pageSize);
        return findPageByCondition(page, vo, page.getOrderByField(), page.isAsc());
    }

    @Override
    public List<ArticleDto> listHotArticle(int pageSize) {
        ArticleConditionVO vo = new ArticleConditionVO();
        vo.setStatus(ArticleStatusEnum.PUNISH.getKey());
        Page<ArticleDto> page = new PageFactory<ArticleDto>().defaultPage();
        page.setSize(pageSize);
        List<ArticleDto> list = articleMapper.listHotArticle(page, vo, page.getOrderByField(), page.isAsc());
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        for(ArticleDto dto:list){
            ConstantFactory.me().getArticlePage(dto);
        }
        return list;
    }

    @Override
    public List<ArticleDto> listRelatedArticle(int pageSize, ArticleDto dto) {
        if (null == dto) {
            return listRandom(pageSize);
        }
        ArticleConditionVO vo = new ArticleConditionVO();
        vo.setStatus(ArticleStatusEnum.PUNISH.getKey());

        Long articleId = dto.getId();

        List<Tags> tagss = tagsMapper.findTagsByArticleId(articleId);

        if (!CollectionUtils.isEmpty(tagss)) {
            List<Long> tagIds = new ArrayList<>();
            for (Tags at : tagss) {
                tagIds.add(at.getId());
            }
            vo.setTagIds(tagIds);
        }
        vo.setTypeId(dto.getTypeId());
        Page<ArticleDto> page = new PageFactory<ArticleDto>().defaultPage();
        page.setSize(pageSize);
        return this.findPageByCondition(page, vo, page.getOrderByField(), page.isAsc());
    }

    @Override
    public Map<String, ArticleDto> getPrevAndNextArticles(Date insertTime) {

        insertTime = null == insertTime ? new Date() : insertTime;
        List<ArticleDto> list = articleMapper.getPrevAndNextArticles(insertTime);
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        for(ArticleDto dto:list){
            ConstantFactory.me().getArticlePage(dto);
        }
        Map<String, ArticleDto> resultMap = new HashMap<>();
        for (ArticleDto entity : list) {
            if (entity.getCreateTime().getTime() < insertTime.getTime()) {

                resultMap.put("prev", entity);
            } else {
                resultMap.put("next",entity);
            }
        }
        return resultMap;

    }

    @Override
    public void doPraise(Long id) {
        String ip = IpUtil.getRealIp(HttpKit.getRequest());
        String key = ip + "_doPraise_" + id;
        ValueOperations<String, Object> operations = redisTemplate.opsForValue();
        if (redisTemplate.hasKey(key)) {
            throw new GunsException("500","一个小时只能点赞一次哈，感谢支持~~");
        }
        ShiroUser user = ShiroKit.getUser();
        ArticleLove love = new ArticleLove();
        if (null != user) {
            love.setUserId(user.getId());
        }
        love.setArticleId(id);
        love.setUserIp(IpUtil.getRealIp(HttpKit.getRequest()));
        love.setLoveTime(new Date());
        love.setCreateTime(new Date());
        love.setUpdateTime(new Date());
        articleLoveMapper.insert(love);
        operations.set(key, id, 1, TimeUnit.HOURS);
    }

    @Override
    public boolean isExist(Long id) {
        Integer count = articleMapper.isExist(id);
        return count != null && count > 0;
    }

    @Override
    public void doOppose(Long id) {
        String ip = IpUtil.getRealIp(HttpKit.getRequest());
        String key = ip + "_doOppose_" + id;
        ValueOperations<String, Object> operations = redisTemplate.opsForValue();
        if (redisTemplate.hasKey(key)) {
            throw new GunsException("500","去你妈的，还踩，踩个粑粑~~");
        }
        ShiroUser user = ShiroKit.getUser();
        ArticleOppose oppose = new ArticleOppose();
        if (null != user) {
            oppose.setUserId(user.getId());
        }
        oppose.setArticleId(id);
        oppose.setUserIp(IpUtil.getRealIp(HttpKit.getRequest()));
        oppose.setOpposeTime(new Date());
        oppose.setCreateTime(new Date());
        oppose.setUpdateTime(new Date());
        articleOpposeMapper.insert(oppose);
        operations.set(key, id, 1, TimeUnit.HOURS);
    }

}
